package password

import (
	"context"
	"fmt"
	"github.com/gofiber/fiber/v2"
	"github.com/redis/go-redis/v9"
	"sg-backend/app/entity/userQuery"
	"sg-backend/app/service/userService"
	"sg-backend/global"
	"sg-backend/utils"
	"time"
)

type RecoverPasswordDto struct {
	Account     string `json:"account"`
	VerityCode  string `json:"verityCode"`
	NewPassword string `json:"newPassword"`
}

// RecoverPassword 用户通过绑定的邮箱号收取验证码恢复重置密码
func RecoverPassword(ctx *fiber.Ctx) error {
	// 定义密码修改参数
	var recoverDto RecoverPasswordDto

	// 从body解释数据
	if err := ctx.BodyParser(&recoverDto); err != nil {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": fmt.Sprintf("数据解释出错！%s", err),
		})
	}

	// 获取用户对象
	// 判断用户是否存在
	qo := &userQuery.GetUserByAccountQo{Account: recoverDto.Account}
	userObj := userService.GetUserByAccount(qo)
	r := global.DB.First(&userObj)
	if r.Error != nil {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": fmt.Sprintf("获取用户对象出错！%v", r.Error),
		})
	}

	// 数据解释完成，开始验证
	// 1，验证 验证码是否正确
	rtx := context.Background()
	verityCode, err := global.Redis.Get(
		rtx,
		fmt.Sprintf("revocerPasswordCode_%v", userObj.Id),
	).Result()
	if err == redis.Nil {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": "验证码已过期，请重新获取验证码！",
		})
	}

	if recoverDto.VerityCode != verityCode {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": "验证码不正确！请确认！",
		})
	}

	// 验证全部完成，开始保存到数据库
	var passwordSalt = utils.GetRandomString(16)
	var encryptPassword = utils.SHAEncrypt(recoverDto.NewPassword + passwordSalt)

	// 保存到数据库
	userObj.PasswordSalt = passwordSalt
	userObj.Password = encryptPassword
	r = global.DB.Save(&userObj)
	if r.Error != nil {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": fmt.Sprintf("新密码保存到数据库失败！%v", r.Error),
		})
	}

	// 删除code
	global.Redis.Del(rtx, fmt.Sprintf("revocerPasswordCode_%v", userObj.Id))

	// 处理完成，返回成功结果
	return ctx.JSON(fiber.Map{
		"status":  "done",
		"message": "密码重置成功！请使用新密码验证登录。",
	})
}

// SendVerityCode 根据用户账号（用户名，邮箱号，手机号）获取账号，然后给邮箱发送验证码
func SendVerityCode(ctx *fiber.Ctx) error {

	// 首先在系统中匹配用户对象
	type recoverType struct {
		Account string `json:"account"`
	}
	var recoverDto recoverType

	// 解释用户账号
	if err := ctx.BodyParser(&recoverDto); err != nil {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": "传参错误！未检测到账号！",
		})
	}

	// 判断用户是否存在
	qo := &userQuery.GetUserByAccountQo{Account: recoverDto.Account}
	userObj := userService.GetUserByAccount(qo)
	if userObj == nil {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": "在系统中无法无法匹配到您输入的账号，请检查您的账号是否正确！",
		})
	}

	// 如果没有邮箱，不允许自主找回密码
	if len(userObj.Email) == 0 {
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": "此用户未设置邮箱，无法自主找回密码，请联系管理员找回！",
		})
	}

	// 获取随机6位数字
	randomCode := utils.GetRandomNumber(6)

	// 存入Redis
	rtx := context.Background()
	redisKey := fmt.Sprintf("revocerPasswordCode_%v", userObj.Id)
	r := global.Redis.Set(rtx, redisKey, randomCode, 10*time.Minute)

	if r.Val() != "OK" {

		// 验证码保存到redis不成功
		global.SLog.Error(fmt.Sprintf("恢复密码的验证码保存到redis失败！%+v", r.Err()))
		return ctx.JSON(fiber.Map{
			"status":  "error",
			"message": "验证码保存到redis失败，请联系管理员处理！",
		})
	}

	// 发送邮件
	emailContents := fmt.Sprintf(
		`%v，您好！<br><br>
				您正在申请重置密码，验证码是：<br><hr><h1 style="color: #326cfe">%v</h1>
				<hr><br>
				验证码将在10分钟后过期，请及时操作！<br><br>
				<p style="color: #bfbfbf">*如果不确定是否您本人操作，请忽略此邮件。</p>`,
		recoverDto.Account, randomCode,
	)

	// 使用go关键字，新起一个线程执行，以免堵塞
	go utils.SendMail(userObj.Email, "找回密码", emailContents)

	// 返回通知
	return ctx.JSON(fiber.Map{
		"status":  "done",
		"message": "验证码发送成功，请登录您的邮箱获取验证码！",
	})
}
